#ifndef unigame_Matrix4x4_h
#define unigame_Matrix4x4_h

#include "Vector4.h"
#include "math/Math.h"
#include <assert.h>
#include "config/Def.h"

#define M_INDEX4(r, c) (((c) << 2) | (r))
namespace unigame
{
	template <class T>
	class Matrix4x4
	{
	public:
		Matrix4x4():m_isIdentity(true)
		{
			memset(m_value, 0, sizeof(T) * 16);
			m_value[0] = 1;
			m_value[5] = 1;
			m_value[10] = 1;
			m_value[15] = 1;
		}

		Matrix4x4(const Matrix4x4<T>& m)
		{
			memcpy(this, &m, sizeof(m));
		}

		Matrix4x4(const T value[16])
		{
			memcpy(m_value, value, sizeof(T) * 16);
			m_isIdentity = false;
		}

		~Matrix4x4(){}

		void inverse(Matrix4x4<T>& m);

		void setValue(const T value[16])
		{
			m_isIdentity = false;
			memcpy(m_value, value, sizeof(T) * 16);
		}

		const T* value() const
		{
			return m_value;
		}

		void setRotationByAngle(const Vector3<T> v)
		{
			v *= MULT_TO_RAD;
			setRotationByRad(v.x, v.y, v.z);
		}

		void setRotationByAngle(const T x, const T y, const T z)
		{
			setRotationByRad(x * MULT_TO_RAD, y * MULT_TO_RAD, x * MULT_TO_RAD);
		}

		void setRotationByRad(const Vector3<T> v)
		{
			setRotation(v.x, v.y, v.z);
		}

		void getRotationByAngle(Vector3<T>& v)
		{
			getRotationByRad(v);
			v *= MULT_TO_ANGLE;
		}

		void multScale(const Vector3<T>& v)
		{
			if(m_isIdentity)
			{
				m_value[M_INDEX4(0, 0)] *= v.x;
				m_value[M_INDEX4(1, 1)] *= v.y;
				m_value[M_INDEX4(2, 2)] *= v.z;
				m_isIdentity = false;
				return;
			}
			m_value[M_INDEX4(0, 0)] *= v.x;
			m_value[M_INDEX4(1, 0)] *= v.x;
			m_value[M_INDEX4(2, 0)] *= v.x;

			m_value[M_INDEX4(0, 1)] *= v.y;
			m_value[M_INDEX4(1, 1)] *= v.y;
			m_value[M_INDEX4(2, 1)] *= v.y;

			m_value[M_INDEX4(0, 2)] *= v.z;
			m_value[M_INDEX4(1, 2)] *= v.z;
			m_value[M_INDEX4(2, 2)] *= v.z;
		}

		void setPosition(const Vector3<T>& v)
		{
			m_isIdentity = false;
			m_value[M_INDEX4(0, 3)] = v.x;
			m_value[M_INDEX4(1, 3)] = v.y;
			m_value[M_INDEX4(2, 3)] = v.z;
		}

		void getRotationByRad(Vector3<T>& v);

		void setRotationByRad(const T x, const T y, const T z);

		const T& operator ()(const int32u row, const int32u col) const
		{
			return m_value[(col << 2) | row];
		}

		T& operator ()(const int32u row, const int32u col)
		{
			m_isIdentity = false;
			return m_value[(col << 2) | row];
		}

		const T& operator [](const int32u index) const
		{
			return m_value[index];
		}

		Matrix4x4<T> operator +(const Matrix4x4<T>& m)
		{
			Matrix4x4<T> temp;
			temp.m_isIdentity = false;
			temp.m_value[M_INDEX4(0, 0)] = m_value[M_INDEX4(0, 0)] + m.m_value[M_INDEX4(0, 0)];
			temp.m_value[M_INDEX4(0, 1)] = m_value[M_INDEX4(0, 1)] + m.m_value[M_INDEX4(0, 1)];
			temp.m_value[M_INDEX4(0, 2)] = m_value[M_INDEX4(0, 2)] + m.m_value[M_INDEX4(0, 2)];
			temp.m_value[M_INDEX4(0, 3)] = m_value[M_INDEX4(0, 3)] + m.m_value[M_INDEX4(0, 3)];

			temp.m_value[M_INDEX4(1, 0)] = m_value[M_INDEX4(1, 0)] + m.m_value[M_INDEX4(1, 0)];
			temp.m_value[M_INDEX4(1, 1)] = m_value[M_INDEX4(1, 1)] + m.m_value[M_INDEX4(1, 1)];
			temp.m_value[M_INDEX4(1, 2)] = m_value[M_INDEX4(1, 2)] + m.m_value[M_INDEX4(1, 2)];
			temp.m_value[M_INDEX4(1, 3)] = m_value[M_INDEX4(1, 3)] + m.m_value[M_INDEX4(1, 3)];

			temp.m_value[M_INDEX4(2, 0)] = m_value[M_INDEX4(2, 0)] + m.m_value[M_INDEX4(2, 0)];
			temp.m_value[M_INDEX4(2, 1)] = m_value[M_INDEX4(2, 1)] + m.m_value[M_INDEX4(2, 1)];
			temp.m_value[M_INDEX4(2, 2)] = m_value[M_INDEX4(2, 2)] + m.m_value[M_INDEX4(2, 2)];
			temp.m_value[M_INDEX4(2, 3)] = m_value[M_INDEX4(2, 3)] + m.m_value[M_INDEX4(2, 3)];

			temp.m_value[M_INDEX4(3, 0)] = m_value[M_INDEX4(3, 0)] + m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(3, 1)] = m_value[M_INDEX4(3, 1)] + m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(3, 2)] = m_value[M_INDEX4(3, 2)] + m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(3, 3)] = m_value[M_INDEX4(3, 3)] + m.m_value[M_INDEX4(3, 3)];

			return temp;
		}

		Matrix4x4<T>& operator +=(const Matrix4x4<T>& m)
		{
			m_isIdentity = false;
			m_value[M_INDEX4(0, 0)] += m.m_value[M_INDEX4(0, 0)];
			m_value[M_INDEX4(0, 1)] += m.m_value[M_INDEX4(0, 1)];
			m_value[M_INDEX4(0, 2)] += m.m_value[M_INDEX4(0, 2)];
			m_value[M_INDEX4(0, 3)] += m.m_value[M_INDEX4(0, 3)];

			m_value[M_INDEX4(1, 0)] += m.m_value[M_INDEX4(1, 0)];
			m_value[M_INDEX4(1, 1)] += m.m_value[M_INDEX4(1, 1)];
			m_value[M_INDEX4(1, 2)] += m.m_value[M_INDEX4(1, 2)];
			m_value[M_INDEX4(1, 3)] += m.m_value[M_INDEX4(1, 3)];

			m_value[M_INDEX4(2, 0)] += m.m_value[M_INDEX4(2, 0)];
			m_value[M_INDEX4(2, 1)] += m.m_value[M_INDEX4(2, 1)];
			m_value[M_INDEX4(2, 2)] += m.m_value[M_INDEX4(2, 2)];
			m_value[M_INDEX4(2, 3)] += m.m_value[M_INDEX4(2, 3)];

			m_value[M_INDEX4(3, 0)] += m.m_value[M_INDEX4(3, 0)];
			m_value[M_INDEX4(3, 1)] += m.m_value[M_INDEX4(3, 1)];
			m_value[M_INDEX4(3, 2)] += m.m_value[M_INDEX4(3, 2)];
			m_value[M_INDEX4(3, 3)] += m.m_value[M_INDEX4(3, 3)];

			return *this;
		}

		Matrix4x4<T>& operator =(const Matrix4x4<T>& m)
		{
			m_isIdentity = m_isIdentity;
			memcpy(this, &m, sizeof(m));
			return *this;
		}

		Matrix4x4<T> operator -()
		{
			Matrix4x4<T> temp;
			temp.m_isIdentity = m_isIdentity;
			temp.m_value[M_INDEX4(0,0)] = -m_value[M_INDEX4(0, 0)];
			temp.m_value[M_INDEX4(0,1)] = -m_value[M_INDEX4(0, 1)];
			temp.m_value[M_INDEX4(0,2)] = -m_value[M_INDEX4(0, 2)];
			temp.m_value[M_INDEX4(0,3)] = -m_value[M_INDEX4(0, 3)];

			temp.m_value[M_INDEX4(1,0)] = -m_value[M_INDEX4(1, 0)];
			temp.m_value[M_INDEX4(1,1)] = -m_value[M_INDEX4(1, 1)];
			temp.m_value[M_INDEX4(1,2)] = -m_value[M_INDEX4(1, 2)];
			temp.m_value[M_INDEX4(1,3)] = -m_value[M_INDEX4(1, 3)];

			temp.m_value[M_INDEX4(2,0)] = -m_value[M_INDEX4(2, 0)];
			temp.m_value[M_INDEX4(2,1)] = -m_value[M_INDEX4(2, 1)];
			temp.m_value[M_INDEX4(2,2)] = -m_value[M_INDEX4(2, 2)];
			temp.m_value[M_INDEX4(2,3)] = -m_value[M_INDEX4(2, 3)];

			temp.m_value[M_INDEX4(3,0)] = -m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(3,1)] = -m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(3,2)] = -m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(3,3)] = -m_value[M_INDEX4(3, 3)];

			return temp;
		}
		
		Matrix4x4<T> operator -(const Matrix4x4<T>& m)
		{
			Matrix4x4<T> temp;
			temp.m_isIdentity = false;
			temp.m_value[M_INDEX4(0, 0)] = m_value[M_INDEX4(0, 0)] - m.m_value[M_INDEX4(0, 0)];
			temp.m_value[M_INDEX4(0, 1)] = m_value[M_INDEX4(0, 1)] - m.m_value[M_INDEX4(0, 1)];
			temp.m_value[M_INDEX4(0, 2)] = m_value[M_INDEX4(0, 2)] - m.m_value[M_INDEX4(0, 2)];
			temp.m_value[M_INDEX4(0, 3)] = m_value[M_INDEX4(0, 3)] - m.m_value[M_INDEX4(0, 3)];

			temp.m_value[M_INDEX4(1, 0)] = m_value[M_INDEX4(1, 0)] - m.m_value[M_INDEX4(1, 0)];
			temp.m_value[M_INDEX4(1, 1)] = m_value[M_INDEX4(1, 1)] - m.m_value[M_INDEX4(1, 1)];
			temp.m_value[M_INDEX4(1, 2)] = m_value[M_INDEX4(1, 2)] - m.m_value[M_INDEX4(1, 2)];
			temp.m_value[M_INDEX4(1, 3)] = m_value[M_INDEX4(1, 3)] - m.m_value[M_INDEX4(1, 3)];

			temp.m_value[M_INDEX4(2, 0)] = m_value[M_INDEX4(2, 0)] - m.m_value[M_INDEX4(2, 0)];
			temp.m_value[M_INDEX4(2, 1)] = m_value[M_INDEX4(2, 1)] - m.m_value[M_INDEX4(2, 1)];
			temp.m_value[M_INDEX4(2, 2)] = m_value[M_INDEX4(2, 2)] - m.m_value[M_INDEX4(2, 2)];
			temp.m_value[M_INDEX4(2, 3)] = m_value[M_INDEX4(2, 3)] - m.m_value[M_INDEX4(2, 3)];

			temp.m_value[M_INDEX4(3, 0)] = m_value[M_INDEX4(3, 0)] - m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(3, 1)] = m_value[M_INDEX4(3, 1)] - m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(3, 2)] = m_value[M_INDEX4(3, 2)] - m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(3, 3)] = m_value[M_INDEX4(3, 3)] - m.m_value[M_INDEX4(3, 3)];

			return temp;
		}

		Matrix4x4<T>& operator -=(const Matrix4x4<T>& m)
		{
			m_isIdentity = false;
			m_value[M_INDEX4(0, 0)] -= m.m_value[M_INDEX4(0, 0)];
			m_value[M_INDEX4(0, 1)] -= m.m_value[M_INDEX4(0, 1)];
			m_value[M_INDEX4(0, 2)] -= m.m_value[M_INDEX4(0, 2)];
			m_value[M_INDEX4(0, 3)] -= m.m_value[M_INDEX4(0, 3)];

			m_value[M_INDEX4(1, 0)] -= m.m_value[M_INDEX4(1, 0)];
			m_value[M_INDEX4(1, 1)] -= m.m_value[M_INDEX4(1, 1)];
			m_value[M_INDEX4(1, 2)] -= m.m_value[M_INDEX4(1, 2)];
			m_value[M_INDEX4(1, 3)] -= m.m_value[M_INDEX4(1, 3)];

			m_value[M_INDEX4(2, 0)] -= m.m_value[M_INDEX4(2, 0)];
			m_value[M_INDEX4(2, 1)] -= m.m_value[M_INDEX4(2, 1)];
			m_value[M_INDEX4(2, 2)] -= m.m_value[M_INDEX4(2, 2)];
			m_value[M_INDEX4(2, 3)] -= m.m_value[M_INDEX4(2, 3)];

			m_value[M_INDEX4(3, 0)] -= m.m_value[M_INDEX4(3, 0)];
			m_value[M_INDEX4(3, 1)] -= m.m_value[M_INDEX4(3, 1)];
			m_value[M_INDEX4(3, 2)] -= m.m_value[M_INDEX4(3, 2)];
			m_value[M_INDEX4(3, 3)] -= m.m_value[M_INDEX4(3, 3)];

			return *this;
		}

		Matrix4x4<T> operator *(T value)
		{
			Matrix4x4<T> temp;
			if(m_isIdentity)
			{
				temp.m_value[0] *= value;
				temp.m_value[5] *= value;
				temp.m_value[10] *= value;
				temp.m_value[15] *= value;
			}
			else
			{
				temp.m_value[M_INDEX4(0, 0)] *= value;
				temp.m_value[M_INDEX4(0, 1)] *= value;
				temp.m_value[M_INDEX4(0, 2)] *= value;
				temp.m_value[M_INDEX4(0, 3)] *= value;

				temp.m_value[M_INDEX4(1, 0)] *= value;
				temp.m_value[M_INDEX4(1, 1)] *= value;
				temp.m_value[M_INDEX4(1, 2)] *= value;
				temp.m_value[M_INDEX4(1, 3)] *= value;

				temp.m_value[M_INDEX4(2, 0)] *= value;
				temp.m_value[M_INDEX4(2, 1)] *= value;
				temp.m_value[M_INDEX4(2, 2)] *= value;
				temp.m_value[M_INDEX4(2, 3)] *= value;

				temp.m_value[M_INDEX4(3, 0)] *= value;
				temp.m_value[M_INDEX4(3, 1)] *= value;
				temp.m_value[M_INDEX4(3, 2)] *= value;
				temp.m_value[M_INDEX4(3, 3)] *= value;
			}

			temp.m_isIdentity = m_isIdentity && value == (T)1;
			return temp;
		}

		Vector4<T> operator *(const Vector4<T> v)
		{
			Vector4<T> temp;
			temp.x = m_value[M_INDEX4(0, 0)] * v.x + m_value[M_INDEX4(0, 1)] * v.y + m_value[M_INDEX4(0, 2)] * v.z + m_value[M_INDEX4(0, 3)] * v.w; 
			temp.y = m_value[M_INDEX4(1, 0)] * v.x + m_value[M_INDEX4(1, 1)] * v.y + m_value[M_INDEX4(1, 2)] * v.z + m_value[M_INDEX4(1, 3)] * v.w; 
			temp.z = m_value[M_INDEX4(2, 0)] * v.x + m_value[M_INDEX4(2, 1)] * v.y + m_value[M_INDEX4(2, 2)] * v.z + m_value[M_INDEX4(2, 3)] * v.w; 
			temp.w = m_value[M_INDEX4(3, 0)] * v.x + m_value[M_INDEX4(3, 1)] * v.y + m_value[M_INDEX4(3, 2)] * v.z + m_value[M_INDEX4(3, 3)] * v.w; 
			return temp;
		}

		Matrix4x4<T> operator *(const Matrix4x4<T>& m)
		{
			if(m_isIdentity)
			{
				return m;
			}
			if(m.m_isIdentity)
			{
				return *this;
			}
			Matrix4x4<T> temp;
			temp.m_isIdentity = false;
			temp.m_value[M_INDEX4(0, 0)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(1, 0)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(2, 0)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(3, 0)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 0)];

			temp.m_value[M_INDEX4(0, 1)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(1, 1)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(2, 1)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(3, 1)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 1)];

			temp.m_value[M_INDEX4(0, 2)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(1, 2)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(2, 2)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(3, 2)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 2)];

			temp.m_value[M_INDEX4(0, 3)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(1, 3)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(2, 3)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(3, 3)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 3)];

			return temp;
		}

		Matrix4x4<T>& operator *=(T value)
		{
			if(m_isIdentity)
			{
				m_value[0] *= value;
				m_value[5] *= value;
				m_value[10] *= value;
				m_value[15] *= value;
			}
			else
			{
				m_value[M_INDEX4(0, 0)] *= value;
				m_value[M_INDEX4(0, 1)] *= value;
				m_value[M_INDEX4(0, 2)] *= value;
				m_value[M_INDEX4(0, 3)] *= value;

				m_value[M_INDEX4(1, 0)] *= value;
				m_value[M_INDEX4(1, 1)] *= value;
				m_value[M_INDEX4(1, 2)] *= value;
				m_value[M_INDEX4(1, 3)] *= value;

				m_value[M_INDEX4(2, 0)] *= value;
				m_value[M_INDEX4(2, 1)] *= value;
				m_value[M_INDEX4(2, 2)] *= value;
				m_value[M_INDEX4(2, 3)] *= value;

				m_value[M_INDEX4(3, 0)] *= value;
				m_value[M_INDEX4(3, 1)] *= value;
				m_value[M_INDEX4(3, 2)] *= value;
				m_value[M_INDEX4(3, 3)] *= value;
			}

			m_isIdentity = m_isIdentity && value == (T)1;
			return *this;
		}

		Matrix4x4<T>& operator *= (const Matrix4x4<T>& m)
		{
			if(m_isIdentity)
			{
				memcpy(this, &m, sizeof(Matrix4x4<T>));
				return *this;
			}
			if(m.m_isIdentity)
			{
				return *this;
			}

			Matrix4x4<T> temp;
			temp.m_value[M_INDEX4(0, 0)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(1, 0)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(2, 0)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 0)];
			temp.m_value[M_INDEX4(3, 0)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 0)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 0)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 0)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 0)];

			temp.m_value[M_INDEX4(0, 1)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(1, 1)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(2, 1)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 1)];
			temp.m_value[M_INDEX4(3, 1)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 1)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 1)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 1)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 1)];

			temp.m_value[M_INDEX4(0, 2)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(1, 2)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(2, 2)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 2)];
			temp.m_value[M_INDEX4(3, 2)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 2)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 2)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 2)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 2)];

			temp.m_value[M_INDEX4(0, 3)] = m_value[M_INDEX4(0, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(0, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(0, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(0, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(1, 3)] = m_value[M_INDEX4(1, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(1, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(1, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(1, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(2, 3)] = m_value[M_INDEX4(2, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(2, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(2, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(2, 3)] * m.m_value[M_INDEX4(3, 3)];
			temp.m_value[M_INDEX4(3, 3)] = m_value[M_INDEX4(3, 0)] * m.m_value[M_INDEX4(0, 3)] + m_value[M_INDEX4(3, 1)] * m.m_value[M_INDEX4(1, 3)] 
			+ m_value[M_INDEX4(3, 2)] * m.m_value[M_INDEX4(2, 3)] + m_value[M_INDEX4(3, 3)] * m.m_value[M_INDEX4(3, 3)];
	
			memcpy(m_value, temp.m_value, sizeof(T) * 16);
			m_isIdentity = false;
			return *this;
		}

		Matrix4x4<T> operator /(T value)
		{
			Matrix4x4<T> temp;
			if(m_isIdentity)
			{
				temp.m_value[0] /= value;
				temp.m_value[5] /= value;
				temp.m_value[10] /= value;
				temp.m_value[15] /= value;
			}
			else
			{
				temp.m_value[M_INDEX4(0, 0)] /= value;
				temp.m_value[M_INDEX4(0, 1)] /= value;
				temp.m_value[M_INDEX4(0, 2)] /= value;
				temp.m_value[M_INDEX4(0, 3)] /= value;

				temp.m_value[M_INDEX4(1, 0)] /= value;
				temp.m_value[M_INDEX4(1, 1)] /= value;
				temp.m_value[M_INDEX4(1, 2)] /= value;
				temp.m_value[M_INDEX4(1, 3)] /= value;

				temp.m_value[M_INDEX4(2, 0)] /= value;
				temp.m_value[M_INDEX4(2, 1)] /= value;
				temp.m_value[M_INDEX4(2, 2)] /= value;
				temp.m_value[M_INDEX4(2, 3)] /= value;

				temp.m_value[M_INDEX4(3, 0)] /= value;
				temp.m_value[M_INDEX4(3, 1)] /= value;
				temp.m_value[M_INDEX4(3, 2)] /= value;
				temp.m_value[M_INDEX4(3, 3)] /= value;
			}

			temp.m_isIdentity = m_isIdentity && value == (T)1;
			return temp;
		}

		Matrix4x4<T>& operator /=(T value)
		{
			if(m_isIdentity)
			{
				m_value[0] /= value;
				m_value[5] /= value;
				m_value[10] /= value;
				m_value[15] /= value;
			}
			else
			{
				m_value[M_INDEX4(0, 0)] /= value;
				m_value[M_INDEX4(0, 1)] /= value;
				m_value[M_INDEX4(0, 2)] /= value;
				m_value[M_INDEX4(0, 3)] /= value;

				m_value[M_INDEX4(1, 0)] /= value;
				m_value[M_INDEX4(1, 1)] /= value;
				m_value[M_INDEX4(1, 2)] /= value;
				m_value[M_INDEX4(1, 3)] /= value;

				m_value[M_INDEX4(2, 0)] /= value;
				m_value[M_INDEX4(2, 1)] /= value;
				m_value[M_INDEX4(2, 2)] /= value;
				m_value[M_INDEX4(2, 3)] /= value;

				m_value[M_INDEX4(3, 0)] /= value;
				m_value[M_INDEX4(3, 1)] /= value;
				m_value[M_INDEX4(3, 2)] /= value;
				m_value[M_INDEX4(3, 3)] /= value;
			}

			m_isIdentity = m_isIdentity && value == (T)1;
			return *this;
		}

		bool isIdentity()
		{
			return m_isIdentity;
		}

	private:
		T m_value[16];
		bool m_isIdentity;
	};

	typedef Matrix4x4<float> Matrix4x4f;

	template <class T>
	void Matrix4x4<T>::setRotationByRad(const T x, const T y, const T z)
	{
		m_isIdentity = false;
		float cy = cosf(y);
		float sy = sinf(y);
		float cx = cosf(x);
		float sx = sinf(x);
		float cz = cosf(z);
		float sz = sinf(z);
		
		float sxsy = sx * sy;
		float sxcy = sx * cy;

		m_value[M_INDEX4(0, 0)] = cy * cz + sxsy * sz;
		m_value[M_INDEX4(0, 1)] = -cy * sz + sxsy * cz;
		m_value[M_INDEX4(0, 2)] = sy * cx;
		m_value[M_INDEX4(0, 3)] = 0;

		m_value[M_INDEX4(1, 0)] = cx * sz;
		m_value[M_INDEX4(1, 1)] = cx * cz;
		m_value[M_INDEX4(1, 2)] = -sx;
		m_value[M_INDEX4(1, 3)] = 0;

		m_value[M_INDEX4(2, 0)] = -cz * sy + sxcy * sz;
		m_value[M_INDEX4(2, 1)] = sy * sz + sxcy * cz;
		m_value[M_INDEX4(2, 2)] = cy * cx;
		m_value[M_INDEX4(2, 3)] = 0;

		m_value[M_INDEX4(3, 0)] = 0;
		m_value[M_INDEX4(3, 1)] = 0;
		m_value[M_INDEX4(3, 2)] = 0;
		m_value[M_INDEX4(3, 3)] = 1;
	}

	template <class T>
	void Matrix4x4<T>::getRotationByRad(Vector3<T>& v)
	{
		if(m_value[M_INDEX4(1, 2)] <= -1.0f)
		{
			v.x = - PI / 2;
		}
		else if(m_value[M_INDEX4(1, 2)] >= 1.0f)
		{
			v.x = PI / 2;
		}
		else
		{
			v.x = asinf(-m_value[M_INDEX4(1, 2)]);
		}
		if(m_value[M_INDEX4(1, 2)] > 0.9999f || m_value[M_INDEX4(1, 2)] < -0.9999f)
		{
			v.z = 0;
			v.y = atan2f(-m_value[M_INDEX4(2, 0)], m_value[M_INDEX4(0, 0)]);
		}
		else
		{
			v.y = atan2f(m_value[M_INDEX4(0, 2)], m_value[M_INDEX4(2, 2)]);
			v.z = atan2f(m_value[M_INDEX4(1, 0)], m_value[M_INDEX4(1, 1)]);
		}
	}

	template <class T>
	void Matrix4x4<T>::inverse(Matrix4x4<T>& m)
	{
		T t0 = m_value[M_INDEX4(2, 2)] * m_value[M_INDEX4(3, 3)] - m_value[M_INDEX4(3, 2)] * m_value[M_INDEX4(2, 3)];
		T t1 = m_value[M_INDEX4(1, 2)] * m_value[M_INDEX4(3, 3)] - m_value[M_INDEX4(3, 2)] * m_value[M_INDEX4(1, 3)];
		T t2 = m_value[M_INDEX4(1, 2)] * m_value[M_INDEX4(2, 3)] - m_value[M_INDEX4(2, 2)] * m_value[M_INDEX4(1, 3)];
		T t3 = m_value[M_INDEX4(0, 2)] * m_value[M_INDEX4(3, 3)] - m_value[M_INDEX4(3, 2)] * m_value[M_INDEX4(0, 3)];
		T t4 = m_value[M_INDEX4(0, 2)] * m_value[M_INDEX4(2, 3)] - m_value[M_INDEX4(2, 2)] * m_value[M_INDEX4(0, 3)];
		T t5 = m_value[M_INDEX4(0, 2)] * m_value[M_INDEX4(1, 3)] - m_value[M_INDEX4(1, 2)] * m_value[M_INDEX4(0, 3)];

		T t6 = m_value[M_INDEX4(2, 0)] * m_value[M_INDEX4(3, 1)] - m_value[M_INDEX4(3, 0)] * m_value[M_INDEX4(2, 1)];
		T t7 = m_value[M_INDEX4(1, 0)] * m_value[M_INDEX4(3, 1)] - m_value[M_INDEX4(3, 0)] * m_value[M_INDEX4(1, 1)];
		T t8 = m_value[M_INDEX4(1, 0)] * m_value[M_INDEX4(2, 1)] - m_value[M_INDEX4(2, 0)] * m_value[M_INDEX4(1, 1)];
		T t9 = m_value[M_INDEX4(0, 0)] * m_value[M_INDEX4(3, 1)] - m_value[M_INDEX4(3, 0)] * m_value[M_INDEX4(0, 1)];
		T t10 = m_value[M_INDEX4(0, 0)] * m_value[M_INDEX4(2, 1)] - m_value[M_INDEX4(2, 0)] * m_value[M_INDEX4(0, 1)];
		T t11 = m_value[M_INDEX4(0, 0)] * m_value[M_INDEX4(1, 1)] - m_value[M_INDEX4(1, 0)] * m_value[M_INDEX4(0, 1)];

		T det = t6 * t5 - t7 * t4 + t8 * t3 + t9 * t2 - t10 * t1 + t11 * t0;
		assert(!Math::floatEqual(det, 0));
		
		m.m_value[M_INDEX4(0, 0)] = m_value[M_INDEX4(1, 1)] * t0 - m_value[M_INDEX4(2, 1)] * t1 + m_value[M_INDEX4(3, 1)] * t2;
		m.m_value[M_INDEX4(0, 1)] = m_value[M_INDEX4(2, 1)] * t3 - m_value[M_INDEX4(0, 1)] * t0 - m_value[M_INDEX4(3, 1)] * t4;
		m.m_value[M_INDEX4(0, 2)] = m_value[M_INDEX4(0, 1)] * t1 - m_value[M_INDEX4(1, 1)] * t3 + m_value[M_INDEX4(3, 1)] * t5;
		m.m_value[M_INDEX4(0, 3)] = m_value[M_INDEX4(1, 1)] * t4 - m_value[M_INDEX4(0, 1)] * t2 - m_value[M_INDEX4(2, 1)] * t5;

		m.m_value[M_INDEX4(1, 0)] = m_value[M_INDEX4(2, 0)] * t1 - m_value[M_INDEX4(1, 0)] * t0 - m_value[M_INDEX4(3, 0)] * t2;
		m.m_value[M_INDEX4(1, 1)] = m_value[M_INDEX4(0, 0)] * t0 - m_value[M_INDEX4(2, 0)] * t3 + m_value[M_INDEX4(3, 0)] * t4;
		m.m_value[M_INDEX4(1, 2)] = m_value[M_INDEX4(1, 0)] * t3 - m_value[M_INDEX4(0, 0)] * t1 - m_value[M_INDEX4(3, 0)] * t5;
		m.m_value[M_INDEX4(1, 3)] = m_value[M_INDEX4(0, 0)] * t2 - m_value[M_INDEX4(1, 0)] * t4 + m_value[M_INDEX4(2, 0)] * t5;

		m.m_value[M_INDEX4(2, 0)] = m_value[M_INDEX4(1, 3)] * t6 - m_value[M_INDEX4(2, 3)] * t7 + m_value[M_INDEX4(3, 3)] * t8;
		m.m_value[M_INDEX4(2, 1)] = m_value[M_INDEX4(2, 3)] * t9 - m_value[M_INDEX4(0, 3)] * t6 - m_value[M_INDEX4(3, 3)] * t10;
		m.m_value[M_INDEX4(2, 2)] = m_value[M_INDEX4(0, 3)] * t7 - m_value[M_INDEX4(1, 3)] * t9 + m_value[M_INDEX4(3, 3)] * t11;
		m.m_value[M_INDEX4(2, 3)] = m_value[M_INDEX4(1, 3)] * t10 - m_value[M_INDEX4(0, 3)] * t8 - m_value[M_INDEX4(2, 3)] * t11;

		m.m_value[M_INDEX4(3, 0)] = m_value[M_INDEX4(2, 2)] * t7 - m_value[M_INDEX4(1, 2)] * t6 - m_value[M_INDEX4(3, 2)] * t8;
		m.m_value[M_INDEX4(3, 1)] = m_value[M_INDEX4(0, 2)] * t6 - m_value[M_INDEX4(2, 2)] * t9 + m_value[M_INDEX4(3, 2)] * t10;
		m.m_value[M_INDEX4(3, 2)] = m_value[M_INDEX4(1, 2)] * t9 - m_value[M_INDEX4(0, 2)] * t7 - m_value[M_INDEX4(3, 2)] * t11;
		m.m_value[M_INDEX4(3, 3)] = m_value[M_INDEX4(0, 2)] * t8 - m_value[M_INDEX4(1, 2)] * t10 + m_value[M_INDEX4(2, 2)] * t11;

		m.m_isIdentity = false;
		det = (T)(1.0 / det);
		m *= det;
	}
}

#endif